/* Copyright (c) 2009, Michael Patraw
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * The name of Michael Patraw may not be used to endorse or promote
 *       products derived from this software without specific prior written
 *       permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Michael Patraw ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Michael Patraw BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#include "snarl.h"

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#include "ai.h"
#include "at.h"
#include "board.h"
#include "display.h"
#include "fps.h"
#include "load.h"
#include "player.h"

static void Snarl_StartGame(int mode, int difficulty);
static void Snarl_StopGame(void);
static void Snarl_Game(int mode);

static void Snarl_StartMainMenu(void);
static void Snarl_StopMainMenu(void);
static void Snarl_BeginMainMenuUpdate(void);
static void Snarl_EndMainMenuUpdate(void);

/*******************************************************************************
 * General
 */

void Snarl_Start(int argc, char * argv[])
{
    char buf[BUFSIZ];
    int i, tmp;

    atStart("SNARL", SNARL_WIDTH, SNARL_HEIGHT);
    atDisableRepeat();
    srand((unsigned)time(NULL));

    /* Read args. */
    for (i = 1; i < argc; ++i) {
        if (strcmp(argv[i], "-c") == 0)
            Load_CustomIcons();
        else if (strcmp(argv[i], "-w") == 0 && i < argc - 1) {
            if (sscanf(argv[++i], "%d", &tmp) == 1)
                Load_SetBoardDimensions(tmp, -1);
        }
        else if (strcmp(argv[i], "-h") == 0 && i < argc - 1) {
            if (sscanf(argv[++i], "%d", &tmp) == 1)
                Load_SetBoardDimensions(-1, tmp);
        }
        else if (strcmp(argv[i], "--help") == 0) {
            
        }
        else {
            printf("Unknown command: '%s'.\n"
                   "Type '%s --help' for usage.\n", argv[i], argv[0]);
        }
    }
}

void Snarl_Stop(void)
{
    atStop();
}

/*******************************************************************************
 * Main Menu
 */
 
enum {
    ONE_PLAYER,
    TWO_PLAYER,
    MULTIPLAYER,
    QUIT
};

int _selected = -1;

static void Snarl_StartMainMenu(void)
{
    _selected = ONE_PLAYER;
}

static void Snarl_StopMainMenu(void)
{

}

void Snarl_MainMenu(void)
{
    AtKey input;

    Snarl_StartMainMenu();
    
    while (atIsRunning()) {
        Snarl_BeginMainMenuUpdate();
        Snarl_EndMainMenuUpdate();
        
        input = atGrabKey();
        
        switch (input.key) {
            case ATK_DOWN:
                ++_selected;
                break;
            case ATK_UP:
                --_selected;
                break;
            case ATK_RETURN: {
                /* Selected an option. */
                switch (_selected) {
                    case ONE_PLAYER: case TWO_PLAYER: case MULTIPLAYER:
                        Snarl_Game(_selected);
                        break;
                    case QUIT:
                        atStopRunning();
                        break;
                }
            }
            default:
                break;
        }
        
        if (_selected < 0)
            _selected += (QUIT + 1);
        else
            _selected %= (QUIT + 1);
    }
    
    Snarl_StopMainMenu();
}

void Snarl_BeginMainMenuUpdate(void)
{
    AtColor one_player_c = ATC_GREY;
    AtColor two_player_c = ATC_GREY;
    AtColor multiplay_c = ATC_GREY;
    AtColor quit_c = ATC_GREY;
    
    if (_selected == ONE_PLAYER) {
        one_player_c = ATC_GREEN;
    }
    
    if (_selected == TWO_PLAYER) {
        two_player_c = ATC_BLUE;
    }
    
    if (_selected == MULTIPLAYER) {
        multiplay_c = ATC_YELLOW;
    }
    
    if (_selected == QUIT) {
        quit_c = ATC_RED;
    }
    
    atDrawString((SNARL_WIDTH - strlen("SNARL")) >> 1, 1,
            ATC_TAN, atGetBgColor(), "SNARL");
    
    atDrawString((SNARL_WIDTH - strlen("\"a game of entrapment\"")) >> 1, 2,
            ATC_TAN, atGetBgColor(), "\"a game of entrapment\"");
    
    atDrawString((SNARL_WIDTH - strlen("Choose a game option:")) >> 1, (SNARL_HEIGHT >> 1) - 2,
            ATC_WHITE, atGetBgColor(), "Choose a game option:");
            
    atDrawString((SNARL_WIDTH - strlen("1 Player")) >> 1, (SNARL_HEIGHT >> 1) + 0,
            one_player_c, atGetBgColor(), "1 Player");
            
    atDrawString((SNARL_WIDTH - strlen("2 Player")) >> 1, (SNARL_HEIGHT >> 1) + 1,
            two_player_c, atGetBgColor(), "2 Player");
            
    atDrawString((SNARL_WIDTH - strlen("Multiplayer")) >> 1, (SNARL_HEIGHT >> 1) + 2,
            multiplay_c, atGetBgColor(), "Multiplayer");
            
    atDrawString((SNARL_WIDTH - strlen("Quit")) >> 1, (SNARL_HEIGHT >> 1) + 3,
            quit_c, atGetBgColor(), "Quit");
}

void Snarl_EndMainMenuUpdate(void)
{
    atUpdate();
    
    atClear();
}

/*******************************************************************************
 * Game
 */

/* Difficulty.
 */
enum {
    DIFF_EASY,
    DIFF_MED,
    DIFF_HARD
};

/* The board.
 */
board_t * _board = NULL;

/* The windows IDs.
 */
unsigned int _info_win_id = -1;
unsigned int _board_win_id = -1;

unsigned int _num_objects = 0;

static void Snarl_StartGame(int mode, int difficulty)
{
    AtWindow * board_win = NULL;
    AtWindow * info_win = NULL;
    player_t * player1 = NULL;
    player_t * player2 = NULL;

    _num_objects = 0;
    
    /* All modes */
    /* Create the board. */
    _board = Board_Create(BOARD_WIDTH, BOARD_HEIGHT);
    /* Create window for the board. */
    board_win = atWindowCreate(BOARD_WIN_WIDTH, BOARD_WIN_WIDTH);
    _board_win_id = Display_RegisterWindow(board_win);
    /* Create window for the info. */
    info_win = atWindowCreate(INFO_WIN_WIDTH, INFO_WIN_HEIGHT);
    _info_win_id = Display_RegisterWindow(info_win);
    
    Fps_Start(); /* Start keeping track of time. */
    
    /* 1 Player mode */
    if (mode == ONE_PLAYER) {
        /* Create the players. */
        player1 = Player_Create(_board, PLAYER_ONE_X, PLAYER_ONE_Y,
                GREEN_TEAM, "Green", HUMAN_CONTROLLED);
        player2 = Player_Create(_board, PLAYER_TWO_X, PLAYER_TWO_Y,
                BLUE_TEAM, "Blue", AI_EASY_CONTROLLED); /* TODO: Set difficulty. */
        
        /* Add the players to game via turn sequence. */
        Player_AddToTurnSequence(player1);
        Player_AddToTurnSequence(player2);
        Player_NextTurn(); /* Initializes the turn sequence. */
    }
    /* 2 Player mode */
    else if (mode == TWO_PLAYER) {
        /* Create the players. */
        player1 = Player_Create(_board, PLAYER_ONE_X, PLAYER_ONE_Y,
                GREEN_TEAM, "Green", HUMAN_CONTROLLED);
        player2 = Player_Create(_board, PLAYER_TWO_X, PLAYER_TWO_Y,
                BLUE_TEAM, "Blue", HUMAN_CONTROLLED);
        
        /* Add the players to game via turn sequence. */
        Player_AddToTurnSequence(player1);
        Player_AddToTurnSequence(player2);
        Player_NextTurn(); /* Initializes the turn sequence. */
    }
}

static void Snarl_StopGame(void)
{
    player_t * player;
    
    /* Clear windows. */
    AtWindow * board_win = Display_RetrieveWindow(_board_win_id);
    AtWindow * info_win = Display_RetrieveWindow(_info_win_id);
    Display_ClearWindows();
    
    atWindowDestroy(board_win);
    atWindowDestroy(info_win);
    
    /* Destroy the board. */
    Board_Destroy(_board);
    _board = NULL;
    
    /* Free players. */
    Player_DestroyAll();
}

static void Snarl_Game(int mode)
{
    AtKey input;
    bool looping = true;

    Snarl_StartGame(mode, DIFF_EASY);
    
    while (atIsRunning() && looping) {
        Snarl_BeginGameUpdate();
        Snarl_EndGameUpdate();

        input = atGrabKey();
        
        switch (input.key) {
            case ATK_UP:
                if (Player_CurrentMoveBy(0, -1) && !Player_CurrentIsAi())
                    Player_NextTurn();
                break;
            case ATK_DOWN:
                if (Player_CurrentMoveBy(0, 1) && !Player_CurrentIsAi())
                    Player_NextTurn();
                break;
            case ATK_LEFT:
                if (Player_CurrentMoveBy(-1, 0) && !Player_CurrentIsAi())
                    Player_NextTurn();
                break;
            case ATK_RIGHT:
                if (Player_CurrentMoveBy(1, 0) && !Player_CurrentIsAi())
                    Player_NextTurn();
                break;
            case 'o':
                if (Player_CurrentPlaceObject() && !Player_CurrentIsAi())
                    Player_NextTurn();
                break;
            case 'O':
                if (Player_CurrentPlaceImmovableObject() && !Player_CurrentIsAi())
                    Player_NextTurn();
                break;
            case 'Q':
                looping = false;
                break;
            default:
                break;
        }
    }
    
    Snarl_StopGame();
}

void Snarl_BeginGameUpdate()
{
    char fps[BUFSIZ];
    int xoffset;
    AtWindow * board_win, * info_win;
    
    /* FPS */
    Fps_EndFrame();
    Fps_BeginFrame();
    snprintf(fps, sizeof(fps), "FPS: %g", Fps_Get());

    /* Check win conditions. */
    Player_CurrentCheckIfLost();
    
    board_win = Display_RetrieveWindow(_board_win_id);
    info_win = Display_RetrieveWindow(_info_win_id);
    
    /* Draw. */
    Board_Draw(_board, _board_win_id);
    Player_DrawEveryone(_board_win_id);
    Player_DrawInformation(_info_win_id);
    
    atWindowDrawStringWrap(info_win, 1, (atWindowHeight(info_win) >> 1) - 3,
            ATC_GREY, atWindowGetBgColor(info_win), "arrows - move");
    atWindowDrawStringWrap(info_win, 1, (atWindowHeight(info_win) >> 1) - 2,
            ATC_GREY, atWindowGetBgColor(info_win), "o - place object");
    atWindowDrawStringWrap(info_win, 1, (atWindowHeight(info_win) >> 1) - 1,
            ATC_GREY, atWindowGetBgColor(info_win), "O - place immovable object");
    atWindowDrawStringWrap(info_win, 1, (atWindowHeight(info_win) >> 1) - 0,
            ATC_GREY, atWindowGetBgColor(info_win), "escape - cancel placement");
    
    atWindowDrawString(info_win, 1, atWindowHeight(info_win) - 1, ATC_GREY,
            atWindowGetBgColor(info_win), fps);
    
    xoffset = SNARL_WIDTH - atWindowWidth(board_win);
    atBlit(xoffset, 0, board_win, 0, 0, -1, -1);
    atBlit(0, 0, info_win, 0, 0, -1, -1);
}

void Snarl_EndGameUpdate(void)
{
    AtWindow * board_win, * info_win;

    board_win = Display_RetrieveWindow(_board_win_id);
    info_win = Display_RetrieveWindow(_info_win_id);

    /* Flip to the new buffer. */
    atUpdate();
    
    /* Clear the backbuffer. */
    atWindowClear(board_win);
    atWindowClear(info_win);
    atClear();
}
